@imenam/mcp-documentor 1.0.44 → 1.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/gui-worker.js +173 -1
- package/dist/src/index.js +18 -0
- package/dist/src/tools/upload-image.d.ts +14 -0
- package/dist/src/tools/upload-image.js +66 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/gui/dist/assets/{arc-Ce3Gb8xA.js → arc-BKRFpV_X.js} +1 -1
- package/gui/dist/assets/architectureDiagram-3BPJPVTR-g3ix86w4.js +36 -0
- package/gui/dist/assets/blockDiagram-GPEHLZMM-DJp0RySg.js +132 -0
- package/gui/dist/assets/{c4Diagram-AHTNJAMY-WNjVDfrm.js → c4Diagram-AAUBKEIU-Dv19Wb0P.js} +1 -1
- package/gui/dist/assets/channel-BrniWeCv.js +1 -0
- package/gui/dist/assets/{chunk-EDXVE4YY-Dg6HQynX.js → chunk-2J33WTMH-BBsfTgNR.js} +1 -1
- package/gui/dist/assets/{chunk-4BX2VUAB-DiqlRlBf.js → chunk-4BX2VUAB-CSXLRZh_.js} +1 -1
- package/gui/dist/assets/{chunk-55IACEB6-w5vdVSko.js → chunk-55IACEB6-BvPtr-ne.js} +1 -1
- package/gui/dist/assets/chunk-727SXJPM-CWUhOtR-.js +206 -0
- package/gui/dist/assets/chunk-AQP2D5EJ-BQ0HicF_.js +231 -0
- package/gui/dist/assets/{chunk-FMBD7UC4-B_8Ah5kO.js → chunk-FMBD7UC4-jHVVH30s.js} +1 -1
- package/gui/dist/assets/{chunk-YZCP3GAM-UAMyKnC1.js → chunk-ND2GUHAM-Bpfb395X.js} +1 -1
- package/gui/dist/assets/{chunk-QZHKN3VN-3KYhwfha.js → chunk-QZHKN3VN-Dx2Yi2S_.js} +1 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-B8_yXQAM.js +1 -0
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-B8_yXQAM.js +1 -0
- package/gui/dist/assets/{cose-bilkent-S5V4N54A-DClmwFbl.js → cose-bilkent-S5V4N54A-Ds421Gqq.js} +1 -1
- package/gui/dist/assets/dagre-BM42HDAG-DlN4DvzI.js +4 -0
- package/gui/dist/assets/diagram-2AECGRRQ-egBqkNYN.js +43 -0
- package/gui/dist/assets/diagram-5GNKFQAL-De9QbkXT.js +10 -0
- package/gui/dist/assets/diagram-KO2AKTUF-B7ViWsaJ.js +3 -0
- package/gui/dist/assets/diagram-LMA3HP47-CWI-hnKY.js +24 -0
- package/gui/dist/assets/{diagram-G4DWMVQ6-uNQmnOVi.js → diagram-OG6HWLK6-qstINSVL.js} +2 -2
- package/gui/dist/assets/{erDiagram-SMLLAGMA-9vfXUDTU.js → erDiagram-TEJ5UH35-CH3pg4ub.js} +1 -1
- package/gui/dist/assets/{flowDiagram-DWJPFMVM-BKu8hLQt.js → flowDiagram-I6XJVG4X-DGKis5au.js} +1 -1
- package/gui/dist/assets/{ganttDiagram-T4ZO3ILL-JKTpXhdp.js → ganttDiagram-6RSMTGT7-BiD0e5Ub.js} +4 -4
- package/gui/dist/assets/gitGraphDiagram-PVQCEYII-7LNV7Qnv.js +106 -0
- package/gui/dist/assets/graph--OzhPTMs.js +1 -0
- package/gui/dist/assets/{index-BzyTjtNB.css → index-BYVxYY90.css} +1 -1
- package/gui/dist/assets/{index-CaPQpXd3.js → index-DBOt1LJt.js} +122 -123
- package/gui/dist/assets/infoDiagram-5YYISTIA-CDjlLZyM.js +2 -0
- package/gui/dist/assets/{ishikawaDiagram-UXIWVN3A-Cf7vBj2E.js → ishikawaDiagram-YF4QCWOH-if4ogb8U.js} +1 -1
- package/gui/dist/assets/{journeyDiagram-VCZTEJTY-DtC9ymAy.js → journeyDiagram-JHISSGLW-DOMmF1XD.js} +1 -1
- package/gui/dist/assets/{kanban-definition-6JOO6SKY-O0fDNBIn.js → kanban-definition-UN3LZRKU-BeqZERBX.js} +1 -1
- package/gui/dist/assets/layout-SsrduOYp.js +1 -0
- package/gui/dist/assets/{linear-Cn1ygZ55.js → linear-BKJMMNN0.js} +1 -1
- package/gui/dist/assets/{mindmap-definition-QFDTVHPH-BJkWNcu5.js → mindmap-definition-RKZ34NQL-B2MDEpXz.js} +9 -9
- package/gui/dist/assets/pieDiagram-4H26LBE5-Cykp2K6j.js +30 -0
- package/gui/dist/assets/quadrantDiagram-W4KKPZXB-DLczui3d.js +7 -0
- package/gui/dist/assets/{requirementDiagram-MS252O5E-D7JQGgq0.js → requirementDiagram-4Y6WPE33-VbvufLF4.js} +1 -1
- package/gui/dist/assets/sankeyDiagram-5OEKKPKP-BUOiIMns.js +40 -0
- package/gui/dist/assets/sequenceDiagram-3UESZ5HK-CB63G6iO.js +162 -0
- package/gui/dist/assets/stateDiagram-AJRCARHV-DvGDV-mj.js +1 -0
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-CSlYTx4_.js +1 -0
- package/gui/dist/assets/{timeline-definition-GMOUNBTQ-BSd1iHP7.js → timeline-definition-PNZ67QCA-DVi9QtSX.js} +1 -1
- package/gui/dist/assets/{vennDiagram-DHZGUBPP-5vVRkiC1.js → vennDiagram-CIIHVFJN-BBbj4Tef.js} +5 -5
- package/gui/dist/assets/wardley-L42UT6IY-Ni3IxkS6.js +161 -0
- package/gui/dist/assets/wardleyDiagram-YWT4CUSO-8K4ncLPD.js +78 -0
- package/gui/dist/assets/{xychartDiagram-5P7HB3ND-CKHTns95.js → xychartDiagram-2RQKCTM6-glUaKKLx.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +2 -1
- package/gui/dist/assets/_baseUniq-BU0U623P.js +0 -1
- package/gui/dist/assets/architectureDiagram-Q4EWVU46-Ces_mPGF.js +0 -36
- package/gui/dist/assets/blockDiagram-DXYQGD6D-B8Y-pXgj.js +0 -132
- package/gui/dist/assets/channel-Bawra3aZ.js +0 -1
- package/gui/dist/assets/chunk-4TB4RGXK-CxKEFtUX.js +0 -206
- package/gui/dist/assets/chunk-OYMX7WX6-BT0nXoHz.js +0 -231
- package/gui/dist/assets/classDiagram-6PBFFD2Q-A3ottNSQ.js +0 -1
- package/gui/dist/assets/classDiagram-v2-HSJHXN6E-A3ottNSQ.js +0 -1
- package/gui/dist/assets/clone-mIFDnjwl.js +0 -1
- package/gui/dist/assets/dagre-KV5264BT-CTaJghw8.js +0 -4
- package/gui/dist/assets/diagram-5BDNPKRD-DDL2xiIH.js +0 -10
- package/gui/dist/assets/diagram-MMDJMWI5-Cd2q0xCp.js +0 -43
- package/gui/dist/assets/diagram-TYMM5635-f5gxEjq1.js +0 -24
- package/gui/dist/assets/gitGraphDiagram-UUTBAWPF-WPLTQc61.js +0 -106
- package/gui/dist/assets/graph-BaMEnwCR.js +0 -1
- package/gui/dist/assets/infoDiagram-42DDH7IO-VapOHi4Y.js +0 -2
- package/gui/dist/assets/layout-CKm9GbtU.js +0 -1
- package/gui/dist/assets/min-CVVJHMPl.js +0 -1
- package/gui/dist/assets/pieDiagram-DEJITSTG-DBvmgJfB.js +0 -30
- package/gui/dist/assets/quadrantDiagram-34T5L4WZ-vWwN725k.js +0 -7
- package/gui/dist/assets/sankeyDiagram-XADWPNL6-V_OhiBi2.js +0 -10
- package/gui/dist/assets/sequenceDiagram-FGHM5R23-C7-ndrI7.js +0 -157
- package/gui/dist/assets/stateDiagram-FHFEXIEX-DtqD-GG8.js +0 -1
- package/gui/dist/assets/stateDiagram-v2-QKLJ7IA2-DhNiUVPk.js +0 -1
- package/gui/dist/assets/wardley-RL74JXVD-Dd64Tq4P.js +0 -162
- package/gui/dist/assets/wardleyDiagram-NUSXRM2D-r6Y_JsSV.js +0 -20
package/dist/src/gui-worker.js
CHANGED
|
@@ -6,10 +6,14 @@ import { ProxyClient } from "@imenam/mcp-gui-interface";
|
|
|
6
6
|
import { IPCMessageSchema } from "../shared/schemas/doc.schema.js";
|
|
7
7
|
import path from "path";
|
|
8
8
|
import fs from "fs";
|
|
9
|
+
import { execFile } from "child_process";
|
|
10
|
+
import { promisify } from "util";
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
9
12
|
import { fileURLToPath } from "url";
|
|
10
13
|
import { createLogger, setupLogging } from "@imenam/mcp-gui-interface";
|
|
11
14
|
import matter from "gray-matter";
|
|
12
15
|
import JSZip from "jszip";
|
|
16
|
+
import puppeteer from "puppeteer-core";
|
|
13
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
18
|
function findProjectRoot(startDir) {
|
|
15
19
|
let current = startDir;
|
|
@@ -48,6 +52,53 @@ const parentCheckInterval = setInterval(() => {
|
|
|
48
52
|
process.exit(0);
|
|
49
53
|
}
|
|
50
54
|
}, 2000);
|
|
55
|
+
// undefined = not yet resolved ; null = not found ; string = path found
|
|
56
|
+
let cachedBrowserPath = undefined;
|
|
57
|
+
async function findSystemBrowser() {
|
|
58
|
+
if (cachedBrowserPath !== undefined)
|
|
59
|
+
return cachedBrowserPath;
|
|
60
|
+
const envPath = process.env.CHROMIUM_PATH?.trim();
|
|
61
|
+
if (envPath) {
|
|
62
|
+
if (fs.existsSync(envPath)) {
|
|
63
|
+
cachedBrowserPath = envPath;
|
|
64
|
+
return envPath;
|
|
65
|
+
}
|
|
66
|
+
logger.warn(`CHROMIUM_PATH is set but the file does not exist: ${envPath}`);
|
|
67
|
+
}
|
|
68
|
+
const candidates = [];
|
|
69
|
+
if (process.platform === 'win32') {
|
|
70
|
+
candidates.push('C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe', 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', 'C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe', 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe');
|
|
71
|
+
}
|
|
72
|
+
else if (process.platform === 'darwin') {
|
|
73
|
+
candidates.push('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
candidates.push('/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser', '/snap/bin/chromium');
|
|
77
|
+
}
|
|
78
|
+
for (const p of candidates) {
|
|
79
|
+
if (fs.existsSync(p)) {
|
|
80
|
+
cachedBrowserPath = p;
|
|
81
|
+
return p;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (process.platform !== 'win32') {
|
|
85
|
+
for (const bin of ['google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser']) {
|
|
86
|
+
try {
|
|
87
|
+
const { stdout } = await execFileAsync('which', [bin]);
|
|
88
|
+
const result = stdout.trim();
|
|
89
|
+
if (result) {
|
|
90
|
+
cachedBrowserPath = result;
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch { /* not found */ }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
cachedBrowserPath = null;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
let localPort = 0;
|
|
101
|
+
let guiPublicUrl = '';
|
|
51
102
|
const app = new Hono();
|
|
52
103
|
let proxyClient = null;
|
|
53
104
|
const sseControllers = new Set();
|
|
@@ -439,6 +490,123 @@ app.get("/api/export-zip/:id", async (c) => {
|
|
|
439
490
|
},
|
|
440
491
|
});
|
|
441
492
|
});
|
|
493
|
+
// PDF generation
|
|
494
|
+
app.get("/api/pdf", async (c) => {
|
|
495
|
+
const project = c.req.query('project');
|
|
496
|
+
const docId = c.req.query('docId');
|
|
497
|
+
const scale = Math.min(2, Math.max(0.1, Number(c.req.query('scale') ?? '1')));
|
|
498
|
+
if (!project || !docId) {
|
|
499
|
+
return c.json({ error: 'project and docId are required' }, 400);
|
|
500
|
+
}
|
|
501
|
+
const executablePath = await findSystemBrowser();
|
|
502
|
+
if (!executablePath) {
|
|
503
|
+
return c.json({
|
|
504
|
+
error: 'no_browser',
|
|
505
|
+
message: 'Aucun navigateur (Chrome, Edge ou Chromium) n\'a été trouvé sur ce système. Installez Google Chrome ou Chromium pour activer la génération PDF.',
|
|
506
|
+
}, 503);
|
|
507
|
+
}
|
|
508
|
+
const encodedPath = docId.split('/').map(encodeURIComponent).join('/');
|
|
509
|
+
// ?__pdf=1 force <base href="/"> côté serveur pour que les assets JS chargent correctement
|
|
510
|
+
// sans passer par le proxy (qui requiert une auth)
|
|
511
|
+
const url = `http://localhost:${localPort}/?__pdf=1#/${encodeURIComponent(project)}/${encodedPath}`;
|
|
512
|
+
let browser;
|
|
513
|
+
try {
|
|
514
|
+
browser = await puppeteer.launch({
|
|
515
|
+
executablePath,
|
|
516
|
+
headless: true,
|
|
517
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
518
|
+
});
|
|
519
|
+
const page = await browser.newPage();
|
|
520
|
+
await page.emulateMediaType('print');
|
|
521
|
+
await page.goto(url, { waitUntil: 'load', timeout: 15000 });
|
|
522
|
+
await page.waitForFunction(() => !!window.__pdfReady && (window.__mermaidPending ?? 0) === 0, { timeout: 15000 });
|
|
523
|
+
const pdfBuffer = await page.pdf({
|
|
524
|
+
format: 'A4',
|
|
525
|
+
scale,
|
|
526
|
+
printBackground: false,
|
|
527
|
+
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
|
|
528
|
+
});
|
|
529
|
+
return new Response(Buffer.from(pdfBuffer), {
|
|
530
|
+
status: 200,
|
|
531
|
+
headers: {
|
|
532
|
+
'Content-Type': 'application/pdf',
|
|
533
|
+
'Content-Disposition': `attachment; filename="${encodeURIComponent(docId.split('/').pop() ?? 'document')}.pdf"`,
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
catch (err) {
|
|
538
|
+
logger.error(`PDF generation failed: ${err.message}`);
|
|
539
|
+
return c.json({ error: 'pdf_generation_failed', message: err.message }, 500);
|
|
540
|
+
}
|
|
541
|
+
finally {
|
|
542
|
+
if (browser)
|
|
543
|
+
await browser.close();
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
// --- Images ---
|
|
547
|
+
const IMAGE_MIME = {
|
|
548
|
+
png: "image/png",
|
|
549
|
+
jpg: "image/jpeg",
|
|
550
|
+
jpeg: "image/jpeg",
|
|
551
|
+
gif: "image/gif",
|
|
552
|
+
webp: "image/webp",
|
|
553
|
+
svg: "image/svg+xml",
|
|
554
|
+
};
|
|
555
|
+
function sanitizeImageFilename(name) {
|
|
556
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
557
|
+
}
|
|
558
|
+
function resolveImageFilename(assetsDir, baseName) {
|
|
559
|
+
const safe = sanitizeImageFilename(baseName);
|
|
560
|
+
if (!fs.existsSync(path.join(assetsDir, safe)))
|
|
561
|
+
return safe;
|
|
562
|
+
const ext = path.extname(safe);
|
|
563
|
+
const base = path.basename(safe, ext);
|
|
564
|
+
let i = 1;
|
|
565
|
+
while (fs.existsSync(path.join(assetsDir, `${base}-${i}${ext}`)))
|
|
566
|
+
i++;
|
|
567
|
+
return `${base}-${i}${ext}`;
|
|
568
|
+
}
|
|
569
|
+
app.post("/api/images", async (c) => {
|
|
570
|
+
const project = c.req.query("project");
|
|
571
|
+
if (!project)
|
|
572
|
+
return c.json({ error: "project is required" }, 400);
|
|
573
|
+
let body;
|
|
574
|
+
try {
|
|
575
|
+
body = await c.req.parseBody();
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
return c.json({ error: "Failed to parse multipart body" }, 400);
|
|
579
|
+
}
|
|
580
|
+
const file = body["file"];
|
|
581
|
+
if (!file || typeof file === "string")
|
|
582
|
+
return c.json({ error: "file field is required" }, 400);
|
|
583
|
+
const assetsDir = path.join(dataDir, "projects", project, "assets");
|
|
584
|
+
fs.mkdirSync(assetsDir, { recursive: true });
|
|
585
|
+
const finalName = resolveImageFilename(assetsDir, file.name ?? "image");
|
|
586
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
587
|
+
fs.writeFileSync(path.join(assetsDir, finalName), buffer);
|
|
588
|
+
return c.json({ url: `/api/images/${project}/${finalName}`, filename: finalName });
|
|
589
|
+
});
|
|
590
|
+
app.get("/api/images/:project", (c) => {
|
|
591
|
+
const project = c.req.param("project");
|
|
592
|
+
const assetsDir = path.join(dataDir, "projects", project, "assets");
|
|
593
|
+
if (!fs.existsSync(assetsDir))
|
|
594
|
+
return c.json([]);
|
|
595
|
+
const files = fs.readdirSync(assetsDir).filter((f) => /\.(png|jpe?g|gif|webp|svg)$/i.test(f));
|
|
596
|
+
return c.json(files.map((f) => ({ filename: f, url: `/api/images/${project}/${f}` })));
|
|
597
|
+
});
|
|
598
|
+
app.get("/api/images/:project/:filename", (c) => {
|
|
599
|
+
const project = c.req.param("project");
|
|
600
|
+
const filename = c.req.param("filename");
|
|
601
|
+
const filePath = path.join(dataDir, "projects", project, "assets", filename);
|
|
602
|
+
if (!fs.existsSync(filePath))
|
|
603
|
+
return c.notFound();
|
|
604
|
+
const ext = path.extname(filename).toLowerCase().slice(1);
|
|
605
|
+
const mime = IMAGE_MIME[ext] ?? "application/octet-stream";
|
|
606
|
+
return new Response(fs.readFileSync(filePath), {
|
|
607
|
+
headers: { "Content-Type": mime },
|
|
608
|
+
});
|
|
609
|
+
});
|
|
442
610
|
// Static files
|
|
443
611
|
app.use("/assets/*", serveStatic({ root: path.relative(process.cwd(), absoluteStaticRoot) }));
|
|
444
612
|
function getHtmlBaseHref(appPath) {
|
|
@@ -454,7 +622,9 @@ app.get("/*", (c, next) => {
|
|
|
454
622
|
try {
|
|
455
623
|
const indexPath = path.join(absoluteStaticRoot, "index.html");
|
|
456
624
|
if (fs.existsSync(indexPath)) {
|
|
457
|
-
|
|
625
|
+
// ?__pdf=1 : accès direct depuis Puppeteer — pas de préfixe proxy dans <base href>
|
|
626
|
+
const isPdfRender = c.req.query('__pdf') === '1';
|
|
627
|
+
const baseHref = isPdfRender ? '/' : getHtmlBaseHref(configuredAppPath);
|
|
458
628
|
const html = fs
|
|
459
629
|
.readFileSync(indexPath, "utf8")
|
|
460
630
|
.replace("<head>", `<head>\n <base href="${baseHref}" />`);
|
|
@@ -504,7 +674,9 @@ async function startServer() {
|
|
|
504
674
|
return;
|
|
505
675
|
}
|
|
506
676
|
const finalPort = result.port;
|
|
677
|
+
localPort = finalPort;
|
|
507
678
|
const publicUrl = result.url || `${proxyUrl}${appPath}`;
|
|
679
|
+
guiPublicUrl = publicUrl;
|
|
508
680
|
logger.info(`Registered with proxy. Public URL: ${publicUrl}, Local Port: ${finalPort}`);
|
|
509
681
|
const cleanupProxy = async () => {
|
|
510
682
|
if (proxyClient)
|
package/dist/src/index.js
CHANGED
|
@@ -37,6 +37,7 @@ import { restoreDocVersion } from "./tools/restore-doc-version.js";
|
|
|
37
37
|
import { reindexEmbeddings } from "./tools/reindex-embeddings.js";
|
|
38
38
|
import { prepareExportBundle } from "./tools/prepare-export-bundle.js";
|
|
39
39
|
import { deleteExportBundle } from "./tools/delete-export-bundle.js";
|
|
40
|
+
import { uploadImage } from "./tools/upload-image.js";
|
|
40
41
|
const __filename = fileURLToPath(import.meta.url);
|
|
41
42
|
const __dirname = path.dirname(__filename);
|
|
42
43
|
function findProjectRoot(startDir) {
|
|
@@ -972,6 +973,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
972
973
|
required: ["id"],
|
|
973
974
|
},
|
|
974
975
|
},
|
|
976
|
+
{
|
|
977
|
+
name: "upload_image",
|
|
978
|
+
description: "Upload an image to a project's assets and get back the markdown link to insert in a document. Provide either 'content' (base64-encoded image) or 'filePath' (absolute path on the server). Returns the URL and a ready-to-use markdown snippet: .",
|
|
979
|
+
inputSchema: {
|
|
980
|
+
type: "object",
|
|
981
|
+
properties: {
|
|
982
|
+
filename: { type: "string", description: "Filename with extension, e.g. 'screenshot.png'" },
|
|
983
|
+
content: { type: "string", description: "Base64-encoded image content (without data: prefix)" },
|
|
984
|
+
filePath: { type: "string", description: "Absolute path to the image file on the server (alternative to content)" },
|
|
985
|
+
project: { type: "string", description: "Target project (defaults to current project)" },
|
|
986
|
+
alt: { type: "string", description: "Alt text for the markdown image link (optional)" },
|
|
987
|
+
},
|
|
988
|
+
required: ["filename"],
|
|
989
|
+
},
|
|
990
|
+
},
|
|
975
991
|
],
|
|
976
992
|
};
|
|
977
993
|
});
|
|
@@ -1041,6 +1057,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1041
1057
|
}
|
|
1042
1058
|
return result;
|
|
1043
1059
|
}
|
|
1060
|
+
case "upload_image":
|
|
1061
|
+
return await uploadImage(storageEngine, args);
|
|
1044
1062
|
default:
|
|
1045
1063
|
throw new Error(`Tool not found: ${name}`);
|
|
1046
1064
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { StorageEngine } from "../core/storage-engine.js";
|
|
2
|
+
export declare function uploadImage(storageEngine: StorageEngine, args: unknown): Promise<{
|
|
3
|
+
isError: boolean;
|
|
4
|
+
content: {
|
|
5
|
+
type: string;
|
|
6
|
+
text: string;
|
|
7
|
+
}[];
|
|
8
|
+
} | {
|
|
9
|
+
content: {
|
|
10
|
+
type: string;
|
|
11
|
+
text: string;
|
|
12
|
+
}[];
|
|
13
|
+
isError?: undefined;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
const UploadImageArgsSchema = z.object({
|
|
5
|
+
filename: z.string().min(1),
|
|
6
|
+
content: z.string().optional(),
|
|
7
|
+
filePath: z.string().optional(),
|
|
8
|
+
project: z.string().optional(),
|
|
9
|
+
alt: z.string().optional(),
|
|
10
|
+
}).refine((d) => d.content !== undefined || d.filePath !== undefined, {
|
|
11
|
+
message: "Either 'content' (base64) or 'filePath' must be provided",
|
|
12
|
+
});
|
|
13
|
+
function sanitizeFilename(name) {
|
|
14
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
15
|
+
}
|
|
16
|
+
function resolveFilename(assetsDir, baseName) {
|
|
17
|
+
const safe = sanitizeFilename(baseName);
|
|
18
|
+
if (!fs.existsSync(path.join(assetsDir, safe)))
|
|
19
|
+
return safe;
|
|
20
|
+
const ext = path.extname(safe);
|
|
21
|
+
const base = path.basename(safe, ext);
|
|
22
|
+
let i = 1;
|
|
23
|
+
while (fs.existsSync(path.join(assetsDir, `${base}-${i}${ext}`)))
|
|
24
|
+
i++;
|
|
25
|
+
return `${base}-${i}${ext}`;
|
|
26
|
+
}
|
|
27
|
+
export async function uploadImage(storageEngine, args) {
|
|
28
|
+
try {
|
|
29
|
+
const validated = UploadImageArgsSchema.parse(args);
|
|
30
|
+
const config = storageEngine.getConfig();
|
|
31
|
+
const project = validated.project ?? config.currentProject;
|
|
32
|
+
const assetsDir = path.join(storageEngine.getDataDir(), "projects", project, "assets");
|
|
33
|
+
fs.mkdirSync(assetsDir, { recursive: true });
|
|
34
|
+
const finalName = resolveFilename(assetsDir, validated.filename);
|
|
35
|
+
const targetPath = path.join(assetsDir, finalName);
|
|
36
|
+
if (validated.content !== undefined) {
|
|
37
|
+
const buffer = Buffer.from(validated.content, "base64");
|
|
38
|
+
fs.writeFileSync(targetPath, buffer);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const src = validated.filePath;
|
|
42
|
+
if (!fs.existsSync(src)) {
|
|
43
|
+
return {
|
|
44
|
+
isError: true,
|
|
45
|
+
content: [{ type: "text", text: `File not found: ${src}` }],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
fs.copyFileSync(src, targetPath);
|
|
49
|
+
}
|
|
50
|
+
const url = `/api/images/${project}/${finalName}`;
|
|
51
|
+
const alt = validated.alt ?? "";
|
|
52
|
+
const markdownLink = ``;
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: "text",
|
|
57
|
+
text: `Image uploaded successfully.\nFilename: ${finalName}\nURL: ${url}\nMarkdown: ${markdownLink}`,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
64
|
+
return { isError: true, content: [{ type: "text", text: `Error uploading image: ${message}` }] };
|
|
65
|
+
}
|
|
66
|
+
}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const APP_VERSION = "1.0.
|
|
1
|
+
export declare const APP_VERSION = "1.0.49";
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated version file - do not edit manually
|
|
2
|
-
export const APP_VERSION = '1.0.
|
|
2
|
+
export const APP_VERSION = '1.0.49';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-
|
|
1
|
+
import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-DBOt1LJt.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(fn(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,F=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<rn)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(on((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|